HEX
Server: Apache/2.4.68 (Debian)
System: Linux as-cs-widget-demo-us-central1 6.1.0-44-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09) x86_64
User: root (0)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: /var/www/kevin-demo/wp-content/plugins/grocerslist/includes/Admin/AjaxController.php
<?php

namespace GrocersList\Admin;

use GrocersList\Jobs\MigrationVisitor;
use GrocersList\Service\ApiClient;
use GrocersList\Service\CreatorSettingsFetcher;
use GrocersList\Service\UrlMappingService;
use GrocersList\Settings\PluginSettings;

class AjaxController
{
    private CreatorSettingsFetcher $creatorSettingsFetcher;
    private SalesPage $salesPage;

    public function __construct(CreatorSettingsFetcher $creatorSettingsFetcher, SalesPage $salesPage) {
        $this->creatorSettingsFetcher = $creatorSettingsFetcher;
        $this->salesPage = $salesPage;
    }

    public function register(): void
    {
        $actions = [
            'grocers_list_get_state' => 'getState',
            'grocers_list_update_api_key' => 'updateApiKey',
            'grocers_list_update_use_linksta_links' => 'updateUseLinkstaLinks',
            'grocers_list_clear_cache' => 'clearCache',
            'grocers_list_clear_settings' => 'clearSettings',
            'grocers_list_get_migration_status' => 'getMigrationStatus',
            'grocers_list_get_link_count_info' => 'getLinkCountInfo',
            'grocers_list_trigger_migrate' => 'triggerMigrate',
            'grocers_list_update_memberships_enabled' => 'updateMembershipsEnabled',
            'grocers_list_get_sales_page_state' => 'getSalesPageState',
            'grocers_list_create_sales_page' => 'createSalesPage',
            'grocers_list_regenerate_sales_page' => 'regenerateSalesPage',
            'grocers_list_add_sales_page_to_menu' => 'addSalesPageToMenu',
            'grocers_list_update_sales_page_menu_item_label' => 'updateSalesPageMenuItemLabel',
            'grocers_list_remove_sales_page_from_menu' => 'removeSalesPageFromMenu',
            'grocers_list_remove_sales_page' => 'removeSalesPage',
        ];

        foreach ($actions as $hook => $method) {
            add_action("wp_ajax_{$hook}", [$this, $method]);
        }
    }

    public function clearSettings(): void
    {
        check_ajax_referer('grocers_list_clear_settings', 'security');

        $this->checkPermission('grocers_list_clear_settings');

        PluginSettings::reset();

        // remove all mappings (effectively undo-ing migration):
        UrlMappingService::reset_mappings();

        // clear creator-settings cached value
        $this->creatorSettingsFetcher->deleteCreatorSettingsTransient();

        wp_send_json_success(['message' => 'All settings cleared']);
    }

    public function clearCache(): void
    {
        check_ajax_referer('grocers_list_clear_cache', 'security');

        $this->checkPermission('grocers_list_clear_cache');

        $this->creatorSettingsFetcher->deleteCreatorSettingsTransient();

        wp_send_json_success(['message' => 'Cache cleared']);
    }


    public function getState(): void
    {
        check_ajax_referer('grocers_list_get_state', 'security');

        $this->checkPermission('grocers_list_get_state');

        wp_send_json_success([
            'apiKey' => PluginSettings::getApiKey(),
            'useLinkstaLinks' => PluginSettings::isUseLinkstaLinksEnabled(),
        ]);
    }

    public function updateApiKey(): void
    {
        check_ajax_referer('grocers_list_update_api_key', 'security');

        $this->checkPermission('grocers_list_update_api_key');
        $apiKey = isset($_POST['apiKey']) ? sanitize_text_field(wp_unslash($_POST['apiKey'])) : '';

        if (strlen($apiKey) < 10) {
            wp_send_json_error(['error' => 'API key too short'], 400);
        }

        if (!ApiClient::validateApiKey($apiKey)) {
            wp_send_json_error(['error' => 'Invalid API key'], 400);
        }

        PluginSettings::setApiKey($apiKey);

        // API key change may indicate new Creator, clear creator-settings cached value
        $this->creatorSettingsFetcher->deleteCreatorSettingsTransient();

        wp_send_json_success(['message' => 'API key updated']);
    }

    public function updateUseLinkstaLinks(): void
    {
        check_ajax_referer('grocers_list_update_use_linksta_links', 'security');

        $this->checkPermission('grocers_list_update_use_linksta_links');
        $enabled = isset($_POST['useLinkstaLinks'])
            && sanitize_text_field(wp_unslash($_POST['useLinkstaLinks'])) === '1';
        PluginSettings::setUseLinkstaLinks($enabled);
        wp_send_json_success(['message' => 'Use Linksta Links setting updated']);
    }

    private function checkPermission(string $nonceAction): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['error' => 'Unauthorized'], 403);
        }
    }

    public function getMigrationStatus(): void
    {
        check_ajax_referer('grocers_list_get_migration_status', 'security');

        $this->checkPermission('grocers_list_get_migration_status');

        wp_send_json_success(MigrationVisitor::getStatus());
    }

    public function getLinkCountInfo(): void
    {
        check_ajax_referer('grocers_list_get_link_count_info', 'security');

        $this->checkPermission('grocers_list_get_link_count_info');

        $countInfo = UrlMappingService::get_link_count_info();
        wp_send_json_success($countInfo);
    }

    public function triggerMigrate(): void
    {
        check_ajax_referer('grocers_list_trigger_migrate', 'security');

        $this->checkPermission('grocers_list_trigger_migrate');

        $migrationInfo = MigrationVisitor::queueMigration();

        wp_send_json_success([
            'success' => true,
            'message' => 'Migration completed',
            'data' => $migrationInfo,
        ]);
    }

    public function updateMembershipsEnabled(): void
    {
        check_ajax_referer('grocers_list_update_memberships_enabled', 'security');

        $this->checkPermission('grocers_list_update_memberships_enabled');

        $enabled = isset($_POST['enabled']) ? sanitize_text_field(wp_unslash($_POST['enabled'])) : '';

        if (!in_array($enabled, ['0', '1'], true)) {
            wp_send_json_error(['error' => 'Invalid enabled parameter - must be "0" or "1"'], 400);
            return;
        }

        $api_key = PluginSettings::getApiKey();

        $response = ApiClient::updateMembershipsEnabled($api_key, $enabled);

        if (is_wp_error($response)) {
            wp_send_json_error(['error' => 'Failed to update memberships enabled setting. Please check your API Key or contact support for help.'], 500);
            return;
        }

        // clear creator-settings cached value, as "settings.memberships.enabled" is included in it
        $this->creatorSettingsFetcher->deleteCreatorSettingsTransient();

        wp_send_json_success(['data' => $enabled, 'message' => 'Memberships enabled setting updated']);
    }

    public function getSalesPageState(): void
    {
        check_ajax_referer('grocers_list_get_sales_page_state', 'security');
        $this->checkPermission('grocers_list_get_sales_page_state');

        wp_send_json_success($this->salesPage->getState());
    }

    public function createSalesPage(): void
    {
        check_ajax_referer('grocers_list_create_sales_page', 'security');
        $this->checkPermission('grocers_list_create_sales_page');

        $slug = isset($_POST['slug']) ? sanitize_title(wp_unslash($_POST['slug'])) : 'membership';

        $result = $this->salesPage->createPage($slug);
        if (isset($result['error'])) {
            wp_send_json_error(['error' => $result['error']], 500);
            return;
        }

        wp_send_json_success($this->salesPage->getState());
    }

    public function regenerateSalesPage(): void
    {
        check_ajax_referer('grocers_list_regenerate_sales_page', 'security');
        $this->checkPermission('grocers_list_regenerate_sales_page');

        $slug = isset($_POST['slug']) ? sanitize_title(wp_unslash($_POST['slug'])) : 'membership';

        $result = $this->salesPage->regeneratePage($slug);
        if (isset($result['error'])) {
            wp_send_json_error(['error' => $result['error']], 500);
            return;
        }

        wp_send_json_success($this->salesPage->getState());
    }

    public function addSalesPageToMenu(): void
    {
        check_ajax_referer('grocers_list_add_sales_page_to_menu', 'security');
        $this->checkPermission('grocers_list_add_sales_page_to_menu');

        $menuId = isset($_POST['menuId']) ? (int) wp_unslash($_POST['menuId']) : 0;
        $label = isset($_POST['label']) ? sanitize_text_field(wp_unslash($_POST['label'])) : 'Membership';

        $result = $this->salesPage->addToMenu($menuId, $label);
        if (isset($result['error'])) {
            wp_send_json_error(['error' => $result['error']], 400);
            return;
        }

        wp_send_json_success($this->salesPage->getState());
    }

    public function updateSalesPageMenuItemLabel(): void
    {
        check_ajax_referer('grocers_list_update_sales_page_menu_item_label', 'security');
        $this->checkPermission('grocers_list_update_sales_page_menu_item_label');

        $label = isset($_POST['label']) ? sanitize_text_field(wp_unslash($_POST['label'])) : '';

        $result = $this->salesPage->updateMenuItemLabel($label);
        if (isset($result['error'])) {
            wp_send_json_error(['error' => $result['error']], 400);
            return;
        }

        wp_send_json_success($this->salesPage->getState());
    }

    public function removeSalesPageFromMenu(): void
    {
        check_ajax_referer('grocers_list_remove_sales_page_from_menu', 'security');
        $this->checkPermission('grocers_list_remove_sales_page_from_menu');

        $this->salesPage->removeFromMenu();
        wp_send_json_success($this->salesPage->getState());
    }

    public function removeSalesPage(): void
    {
        check_ajax_referer('grocers_list_remove_sales_page', 'security');
        $this->checkPermission('grocers_list_remove_sales_page');

        $this->salesPage->removePage();
        wp_send_json_success($this->salesPage->getState());
    }
}